﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace FlashToolKit
{
	/// <summary>
	/// Splits SWF file to parts
	/// </summary>
	public class Decomposer
	{
		private readonly string _filePath;
		private int _curPos;
		private StreamWriter _mapFile;
		private string _dirName;
		private byte[] _content;
		private int _curIndex;

		/// <summary>
		/// Creates new instance of Decomposer class.
		/// </summary>
		/// <param name="filePath">
		/// Full path to target file.
		/// </param>
		public Decomposer(string filePath)
		{
			_filePath = filePath;
		}

		/// <summary>
		/// Split SWF file and save result using default path and file names
		/// </summary>
		public void Decompose ()
		{
			FileStream fs;
			try
			{
				fs = File.OpenRead(_filePath);
			}
			catch (FileNotFoundException)
			{
				return;
			}

			_dirName = _filePath.Substring(0, _filePath.LastIndexOf('.'));
			Directory.CreateDirectory(_dirName);
			_dirName += "\\";

			BinaryReader reader = new BinaryReader(fs);

			if (reader.PeekChar() == 0x46)
			{
				_mapFile = File.CreateText(_dirName + "!map.map");
				_curPos = 0;
				_content = reader.ReadBytes((int)fs.Length);
				int len = CalcRECTLen(_content[8]);
				SaveToFile(_dirName + "!header.bin", len + 12);
				_mapFile.WriteLine("!header.bin");
				_curPos += len + 12;
				_curIndex = 0;
				while ((_content[_curPos] != 0) || (_content[_curPos + 1] != 0))
				{
					ProcessNextRecord();
				}
				_mapFile.Close();
			}

			fs.Close();
		}

		private static int CalcRECTLen(byte inp)
		{
			int minLen = (inp >> 3)*4 + 5;
			int actLen;
			for (actLen = 0; actLen * 8 < minLen; actLen++)
			{
			}
			return actLen;
		}

		private int GetRecordLen()
		{
			byte headerLen = (byte) (_content[_curPos] & 0x3f);
			if (headerLen == 0x3f)
			{
				return BitConverter.ToInt32(_content, _curPos + 2) + 6;
			}
			return headerLen+2;
		}

		private short GetRecordType()
		{
			ushort header = BitConverter.ToUInt16(_content, _curPos);
			return (short) (header >> 6);
		}

		private void SaveToFile(string fileName, int count)
		{
			FileStream fs;
			try
			{
				fs = new FileStream(fileName, FileMode.Create);
			}
			catch(DirectoryNotFoundException)
			{
				Directory.CreateDirectory(fileName.Substring(0, fileName.LastIndexOf('\\')));
				fs = new FileStream(fileName, FileMode.Create);
			}
			
			var writer = new BinaryWriter(fs);
			writer.Write(_content,_curPos,count);
			writer.Close();
			writer.Dispose();
		}

		private string GetFileNumName ()
		{
			string name = _curIndex.ToString();
			while (name.Length < 5)
			{
				name = "0" + name;
			}
			return name;
		}

		private string GetFileNameStr(short type)
		{
			return Convert.TagTypeToStr(type) + "\\" + GetFileNumName();
		}

		private void ProcessNextRecord()
		{
			int len = GetRecordLen();
			short type = GetRecordType();
			string filename = GetFileNameStr(type) + ".bin";
			SaveToFile(_dirName + filename, len);
			_mapFile.WriteLine(filename);
			_curPos += len;
			_curIndex++;
		}
	}
}
